package com.xyy.saas.payment.service.impl;

import cn.hutool.http.HttpUtil;
import com.alibaba.dubbo.config.annotation.Reference;
import com.alibaba.fastjson.JSON;
import com.xyy.ec.order.backend.order.query.dto.OrderDto;
import com.xyy.framework.redis.core.RedisClient;
import com.xyy.saas.payment.cores.api.PaymentLoanApi;
import com.xyy.saas.payment.cores.constants.Constants;
import com.xyy.saas.payment.cores.enums.XydInOutTypeEnum;
import com.xyy.saas.payment.cores.vo.ResultVO;
import com.xyy.saas.payment.cores.vo.XydGetFileDto;
import com.xyy.saas.payment.dao.mapper.RefundOrderMapper;
import com.xyy.saas.payment.dao.mapper.XydCheckDataMapper;
import com.xyy.saas.payment.dao.mapper.XydRepayPlanDetailMapper;
import com.xyy.saas.payment.dao.mapper.XydRepayPlanMapper;
import com.xyy.saas.payment.dao.model.*;
import com.xyy.saas.payment.exception.PaymentException;
import com.xyy.saas.payment.manage.cores.vo.ResultPageVo;
import com.xyy.saas.payment.pojo.dto.XydCheckFileRecord;
import com.xyy.saas.payment.pojo.vo.XydCheckDataVo;
import com.xyy.saas.payment.pojo.vo.XydRepayPlanDetailVo;
import com.xyy.saas.payment.pojo.vo.XydRepayPlanVo;
import com.xyy.saas.payment.rpc.EcOrderRemote;
import com.xyy.saas.payment.service.XydLoanService;
import com.xyy.saas.payment.trace.AlarmService;
import com.xyy.saas.payment.trace.DingTalkMessage;
import com.xyy.saas.payment.util.DateUtil;
import com.xyy.saas.payment.util.ExportExcelUtil;
import com.xyy.saas.payment.util.PojoConvertUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.collections.CollectionUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import javax.servlet.http.HttpServletResponse;
import java.io.*;
import java.util.*;
import java.util.stream.Collectors;


/**
 * @author liujf
 */
@Slf4j
@Service
public class XydLoanServiceImpl implements XydLoanService {
    @Reference(version = "1.0.0")
    private PaymentLoanApi paymentLoanApi;

    public static final String DOWNLOAD_PATH_PREFIX = "/tmp/xyd_checkFile_download";

    @Resource
    private XydCheckDataMapper xydCheckDataMapper;
    @Resource
    private XydRepayPlanMapper xydRepayPlanMapper;

    @Resource
    private XydRepayPlanDetailMapper xydRepayPlanDetailMapper;

    @Autowired
    private AlarmService alarmService;

    @Autowired
    private RedisClient redisClient;

    @Autowired
    private RefundOrderMapper refundOrderMapper;
    @Autowired
    private EcOrderRemote ecOrderRemote;


    @Override
    public void downloadAndSaveFinances(String checkDate) {
        downloadAndSaveFinances(Constants.FINANCE_KEEPER_FILE, checkDate, Constants.XYD_MONTHLY);
    }
    @Override
    public void downloadAndSaveFinances(String fileType, String checkDate, String checkCycle) {
        log.info("XydLoanServiceImpl#downloadAndSaveFinances start");

        List<String> filePathList = new ArrayList<>();
        String logPrefix = "小雨点对账";
        StringJoiner summary = new StringJoiner("\r\n");
        String redisKey = "xyd:check:file:download";

        try {

            // 设置过期时间，防止解锁失败
            if (redisClient.exists(redisKey) || !redisClient.setEx(redisKey, "1", 3600)) {
                log.info("downloadAndSaveFinances setEx failed");
                return;
            }

            // 获取记账文件地址
            ResultVO<List<XydGetFileDto>> resultVO = paymentLoanApi.getXydCheckFile(Constants.FINANCE_KEEPER_FILE, checkDate, Constants.XYD_MONTHLY);
            log.info("{} downloadAndSaveFinances resultVO:{}", logPrefix, JSON.toJSONString(resultVO));
            if (resultVO == null || resultVO.isFail()) {
                throw new PaymentException("获取文件地址失败" + JSON.toJSONString(resultVO));
            }

            List<XydGetFileDto> fileDtoList = resultVO.getResult();
            if (CollectionUtils.isEmpty(fileDtoList)) {
                return;
            }

            String dateStr = checkDate.replace("-", "");
            int i = 0;
            for (XydGetFileDto fileDto : fileDtoList) {
                if (StringUtils.isEmpty(fileDto.getFileUrl())) {
                    continue;
                }
                String fileName = StringUtils.isEmpty(fileDto.getFileName()) ? dateStr + (i++) + ".csv" : fileDto.getFileName();
                String filePath = DOWNLOAD_PATH_PREFIX + File.separator + dateStr + File.separator + fileName;

                long start = System.currentTimeMillis();
                // 下载文件到本地
                long byteCount = HttpUtil.downloadFile(fileDto.getFileUrl(), new File(filePath), 60000);
                long end1 = System.currentTimeMillis();
                summary.add("对账文件下载耗时：" + (end1 - start) + "ms" + " 文件大小" + (byteCount / 1024) + "k");

                filePathList.add(filePath);

                // 解析文件存储到mysql
                analysisAndSaveData(logPrefix, filePath, summary);
                long end2 = System.currentTimeMillis();
                summary.add("对账文件解析并落库耗时：" + (end2 - end1) + "ms");
            }
        } catch (Exception e) {
            log.error("{} downloadAndSaveFinances error", logPrefix, e);
            summary.add("对账文件下载处理异常！");
        } finally {
            // 删除本地文件？？
            redisClient.del(redisKey);
        }

        log.info("XydLoanServiceImpl#downloadAndSaveFinances end");

        // 发送消息
        DingTalkMessage talkMessage = DingTalkMessage.builder()
                .type("小雨点对账文件解析")
                .method("XydLoanServiceImpl.downloadAndSaveFinances")
                .msg("\r\n小雨点对账文件解析汇总：\r\n" + summary)
                .build();
        alarmService.alarm(talkMessage);
    }

    @Override
    public ResultPageVo<List<XydCheckDataVo>> queryAccountList(XydCheckDataQueryParam param) {
        if (param.getTradeEndDate() != null) {
            param.setTradeEndDate(DateUtil.getNextDayZero(param.getTradeEndDate()));
        }
        param.calcOffset();
        // 查询出总数
        long total = xydCheckDataMapper.count(param);
        if (0 == total) {
            return ResultPageVo.createSuccess();
        }

        // 查询数据
        List<XydCheckDataPo> dataPos = xydCheckDataMapper.queryPageList(param);
        return ResultPageVo.createSuccess(param.getPage(), param.getPageSize(), total, PojoConvertUtil.convertXydDataVo(dataPos));

    }

    @Override
    public ResultPageVo<List<XydRepayPlanVo>> queryRepayPlanList(XydRepayPlanQueryParam param) {
        if (param.getPayEndTime() != null) {
            param.setPayEndTime(DateUtil.getNextDayZero(param.getPayEndTime()));
        }

        if (param.getDueEndDateActual() != null) {
            param.setDueEndDateActual(DateUtil.getNextDayZero(param.getDueEndDateActual()));
        }

        param.calcOffset();
        // 查询出总数
        long total = xydRepayPlanMapper.count(param);
        if (0 == total) {
            return ResultPageVo.createSuccess();
        }
        // 查询数据
        List<XydRepayPlanPo> dataPos = xydRepayPlanMapper.queryPageList(param);
        return ResultPageVo.createSuccess(param.getPage(), param.getPageSize(), total, PojoConvertUtil.convertXydRepayPlanVo(dataPos));
    }

    @Override
    public List<XydRepayPlanDetailVo> queryRepayPlanDetail(String payNo) {
        if (StringUtils.isEmpty(payNo)) {
            return Collections.EMPTY_LIST;
        }

        return PojoConvertUtil.convertXydRepayPlanDetailVo(xydRepayPlanDetailMapper.selectByPayNo(payNo));
    }

    @Override
    public void exportAccountList(XydCheckDataQueryParam param, HttpServletResponse response) {
        try {
            if (param.getTradeEndDate() != null) {
                param.setTradeEndDate(DateUtil.getNextDayZero(param.getTradeEndDate()));
            }
            log.info("exportAccountList param:{}", JSON.toJSONString(param));
            long count = xydCheckDataMapper.count(param);

            int pageSize = 200;
            long pages = (count + pageSize - 1) / pageSize;

            List<XydCheckDataVo> checkDataVos = new ArrayList<>((int) count);
            param.setPageSize(pageSize);
            for (int page = 1; page <= pages; page++) {
                int offset = (page - 1) * pageSize;
                param.setOffset(offset);
                checkDataVos.addAll(PojoConvertUtil.convertXydDataVo(xydCheckDataMapper.queryPageList(param)));
            }

            String excelFileName = "小雨点专户查询.xlsx";
            String sheetName = "专户查询";
            String headers[] = com.xyy.saas.payment.constants.Constants.XYD_SPECIAL_ACCOUNT;
            String fieldNames[] = com.xyy.saas.payment.constants.Constants.XYD_SPECIAL_ACCOUNT_FIELD;
            ExportExcelUtil.createExcelDateFormat2(response, excelFileName, sheetName, headers, fieldNames, checkDataVos);
        } catch (Exception e) {
            log.error("exportAccountList error:{}", JSON.toJSONString(param), e);
        }
    }

    @Override
    public void exportRepayPlanList(XydRepayPlanQueryParam param, HttpServletResponse response) {
        try {
            if (param.getPayEndTime() != null) {
                param.setPayEndTime(DateUtil.getNextDayZero(param.getPayEndTime()));
            }

            if (param.getDueEndDateActual() != null) {
                param.setDueEndDateActual(DateUtil.getNextDayZero(param.getDueEndDateActual()));
            }
            log.info("exportRepayPlanList param:{}", JSON.toJSONString(param));
            long count = xydRepayPlanMapper.count(param);

            int pageSize = 200;
            long pages = (count + pageSize - 1) / pageSize;

            List<XydRepayPlanVo> checkDataVos = new ArrayList<>((int) count);
            param.setPageSize(pageSize);
            for (int page = 1; page <= pages; page++) {
                int offset = (page - 1) * pageSize;
                param.setOffset(offset);
                checkDataVos.addAll(PojoConvertUtil.convertXydRepayPlanVo(xydRepayPlanMapper.queryPageList(param)));
            }

            String excelFileName = "小雨点还款计划.xlsx";
            String sheetName = "还款计划";
            String headers[] = com.xyy.saas.payment.constants.Constants.XYD_REPAY_PLAN;
            String fieldNames[] = com.xyy.saas.payment.constants.Constants.XYD_REPAY_PLAN_FIELD;
            ExportExcelUtil.createExcelDateFormat2(response, excelFileName, sheetName, headers, fieldNames, checkDataVos);
        } catch (Exception e) {
            log.error("exportRepayPlanList error:{}", JSON.toJSONString(param), e);
        }
    }

    private void analysisAndSaveData(String logPrefix, String filePath, StringJoiner summary) throws IOException {
        BufferedReader br = null;
        try {
            if (!new File(filePath).exists()) {
                log.info("{} 文件[{}]不存在，不进行解析处理", logPrefix, filePath);
                return;
            }
            int total = 0;
            br = new BufferedReader(new InputStreamReader(new FileInputStream(filePath)));
            String line;
            List<XydCheckFileRecord> records = new ArrayList<>();
            int currLine = 0;
            while ((line = br.readLine()) != null) {
                if (++currLine <= 1) {
                    continue;
                }
                XydCheckFileRecord record = parseRecord(logPrefix, line);
                if (null == record) {
                    continue;
                }
                records.add(record);

                if (records.size() % 200 == 0) {
                    batchSave(logPrefix, records);
                    records = new ArrayList<>();
                }
                total++;
            }
            if (!records.isEmpty()) {
                batchSave(logPrefix, records);
            }
            summary.add("文件" + filePath + " 总行数 " + total);
            log.info("{} analysisAndSaveData#analysis:文件{} 解析行数{} ", logPrefix, filePath, total);
        } catch (Exception e) {
            throw e;
        } finally {
            if (null != br) {
                br.close();
            }
        }
    }

    private XydCheckFileRecord parseRecord(String logPrefix, String line) {
        String[] cols = line.split(",");
        if (cols.length != 15) {
            log.error("{} parseRecord invalid {}", logPrefix, line);
            return null;
        }

        try {
            XydCheckFileRecord record = new XydCheckFileRecord();
            String accountTypeStr = cols[0];
            if (StringUtils.isNotBlank(accountTypeStr)) {
                record.setAccountType(Integer.valueOf(accountTypeStr));
            }

            record.setTradeAccount(cols[4]);
            record.setTradeCode(cols[5]);
            String tradeDateStr = cols[6];
            if (StringUtils.isNotBlank(tradeDateStr)) {
                record.setTradeDate(DateUtil.parase(tradeDateStr, DateUtil.DATEFORMATDAY));
            }
            String tradeTypeStr = cols[7];
            if (StringUtils.isNotBlank(tradeTypeStr)) {
                record.setTradeType(Integer.valueOf(tradeTypeStr));
            }
            record.setOrderPayNo(cols[9]);
            record.setRefundPayNo(cols[10]);
            record.setInOutType(cols[11]);
            record.setTradeAmt(cols[12]);
            record.setTradeRivalAccount(cols[13]);
            record.setTradeRivalName(cols[14]);
            return record;
        } catch (Exception e) {
            log.error("{} parseRecord error {}", logPrefix, line, e);
            return null;
        }
    }

    private void batchSave(String logPrefix, List<XydCheckFileRecord> records) {
        List<String> tradeCodes = records.stream().map(r -> r.getTradeCode()).collect(Collectors.toList());
        Map<String, Long> tradeCodeMap = xydCheckDataMapper.selectByTradeCodes(tradeCodes).stream().collect(Collectors.toMap(p -> p.getTradeCode() + p.getAccountType(), p -> p.getId(), (o, n) -> o));

        // 查询支付单号和退款流水号对应的信息
        List<XydCheckDataPo> allDataPos = buildCheckDataPos(records);


        List<XydCheckDataPo> insertList = new ArrayList<>();
        List<XydCheckDataPo> updateList = new ArrayList<>();
        // 更新和插入
        for (XydCheckDataPo dataPo : allDataPos) {
            Long id = tradeCodeMap.get(dataPo.getTradeCode() + dataPo.getAccountType());
            if (id == null) {
                insertList.add(dataPo);
            } else {
                dataPo.setId(id);
                updateList.add(dataPo);
            }
        }


        if (!insertList.isEmpty()) {
            xydCheckDataMapper.batchInsert(insertList);
        }

        if (!updateList.isEmpty()) {
            xydCheckDataMapper.batchUpdate(updateList);
        }

        log.info("{} batchSave insert:{} update:{}", logPrefix, insertList.size(), updateList.size());
    }

    private List<XydCheckDataPo> buildCheckDataPos(List<XydCheckFileRecord> records) {
        List<XydCheckDataPo> dataPos = new ArrayList<>(records.size());
        List<String> orderPayNoList = new ArrayList<>(records.size());
        List<String> refundPayNoList = new ArrayList<>();

        for (XydCheckFileRecord record : records) {
            if (StringUtils.isNotEmpty(record.getOrderPayNo())) {
                orderPayNoList.add(record.getOrderPayNo());
            }
            if (StringUtils.isNotEmpty(record.getRefundPayNo())) {
                refundPayNoList.add(record.getRefundPayNo());
            }
        }

        Map<String, RefundOrder> refundOrderMap = new HashMap<>();
        Map<String, OrderDto> orderDtoMap = new HashMap<>();
        // 查询退款信息
        if (!refundPayNoList.isEmpty()) {
            List<RefundOrder> refundOrders = refundOrderMapper.selectByRefundNos(refundPayNoList);
            for (RefundOrder refundOrder : refundOrders) {
                refundOrderMap.put(refundOrder.getRefundNo(), refundOrder);
            }
        }

        // 查询订单信息
        List<OrderDto> orderDtoList = ecOrderRemote.queryOrderListByPayNo(orderPayNoList);
        for (OrderDto orderDto : orderDtoList) {
            orderDtoMap.put(orderDto.getTargetOrderId(), orderDto);
        }

        // 填充数据
        for (XydCheckFileRecord record : records) {
            XydCheckDataPo dataPo = new XydCheckDataPo();
            dataPo.setAccountType(record.getAccountType());
            dataPo.setTradeAccount(record.getTradeAccount());
            dataPo.setTradeCode(record.getTradeCode());
            dataPo.setTradeDate(record.getTradeDate());
            dataPo.setTradeType(record.getTradeType());
            dataPo.setOrderPayNo(record.getOrderPayNo());
            dataPo.setRefundPayNo(record.getRefundPayNo());
            dataPo.setInOutType(XydInOutTypeEnum.getCodeByName(record.getInOutType()));
            dataPo.setTradeAmt(record.getTradeAmt());
            dataPo.setTradeRivalAccount(record.getTradeRivalAccount());
            dataPo.setTradeRivalName(record.getTradeRivalName());

            OrderDto orderDto = orderDtoMap.get(record.getOrderPayNo());
            if (orderDto != null) {
                dataPo.setOrderNo(orderDto.getOrderNo());
                dataPo.setOrgId(orderDto.getOrgId());
                dataPo.setOrgName(orderDto.getCompanyName());
                dataPo.setMerchantId(orderDto.getMerchantId());
                dataPo.setMerchantName(orderDto.getMerchantName());
            }

            RefundOrder refundOrder = refundOrderMap.get(record.getRefundPayNo());
            if (refundOrder != null) {
                dataPo.setRefundNo(refundOrder.getBusinessRefundNo());
            }
            dataPos.add(dataPo);
        }

        return dataPos;
    }


}
